package handler

import (
	"context"
	"fmt"
	"github.com/golang/protobuf/proto"
	"github.com/golang/protobuf/ptypes/empty"
	"github.com/golang/protobuf/ptypes/wrappers"
	"time"

	context2 "kop/framework/context"
	"kop/framework/errors"
	"kop/libs/redis"
	"kop/modules/item/consts"
	"kop/modules/port/configuration"
	"kop/pb"
	"kop/util/cd"
	"kop/util/date"

	awardM "kop/modules/award/models"
	itemC "kop/modules/item/client"
	moneyC "kop/modules/money/client"
	roleC "kop/modules/role/client"
)

type PortServiceHandler struct {
	context2.Context
}

func (h *PortServiceHandler) Ports(ctx context.Context, _ *empty.Empty, resp *pb.PortsResp) error {

	var role = roleC.GetRole(h.GetRoleID(ctx))

	var today = date.Date(time.Now())
	var mess = getPorts(role)

	if mess.Date != today {

		mess.Date = today
		mess.Ports, mess.PortID = new3(role), 0
		setPorts(role.RoleID, mess)

		resp.Ports = mess.Ports

	} else if mess.PortID > 0 {

		resp.Ports = mess.Ports[0:1] // 只显示第1个
	} else {

		resp.Ports = mess.Ports[0:3]
	}

	resp.PortID = mess.PortID
	resp.Num, resp.CD = getInvestNum(role)
	resp.Num2 = maxNum(role)
	return nil
}

func (h *PortServiceHandler) Choose(ctx context.Context, req *wrappers.Int32Value, _ *empty.Empty) error {

	var role = roleC.GetRole(h.GetRoleID(ctx))
	var data = getPorts(role)

	if data.PortID != 0 {
		return errors.ParamError
	}

	var ports = make([]*pb.Port, 0, len(data.Ports))
	for _, v := range data.Ports {
		if v.PortID == req.Value {
			ports = append(ports, v)
			break
		}
	}

	if len(ports) == 0 {
		return errors.ParamError
	}

	for _, v := range data.Ports {
		if v.PortID != req.Value {
			ports = append(ports, v)
		}
	}

	data.Ports = ports
	data.PortID = req.Value
	setPorts(role.RoleID, data)
	return nil
}

func (h *PortServiceHandler) Invest(ctx context.Context, _ *empty.Empty, resp *pb.InvestResp) error {

	var role = roleC.GetRole(h.GetRoleID(ctx))

	if err := investNumSub1(role); err != nil {
		return err
	}

	var data = getPorts(role)

	var port *pb.Port
	for _, v := range data.Ports {
		if v.PortID == data.PortID {
			port = v
			break
		}
	}

	if port == nil {
		return errors.ParamError
	}

	var err error
	if resp.Money, err = moneyC.SubCoin(ctx, port.Coin); err != nil {
		return err
	}

	var gold = port.Gold
	resp.Award = &pb.Award{}

	var conf = configuration.GetByPortID(port.PortID)
	if conf.GoldRate > 0 {
		gold += int32(float64(gold) * conf.GoldRate)
	} else {
		resp.Award.AddRawItem(&pb.RawItem{ItemID: conf.ItemID, Number: conf.ItemNumber})
	}

	if port.Num == 49 { // 升级

		var costConf = configuration.GetByRoleLevel(role.Level)
		gold += costConf.LevelUpGold + int32(float64(costConf.LevelUpGold)*conf.LevelUpGoldRate)

		port.Num = 0
		port.Lv += 1

		data.PortID = 0
		refresh(role.Level, data)
		resp.Ports = data.Ports[:3]

	} else {

		port.Num += 1
	}

	resp.Port = port
	setPorts(role.RoleID, data)

	resp.Award.AddRawItem(&pb.RawItem{ItemID: consts.Gold, Number: gold})
	if err := awardM.SendAward(role, resp.Award); err != nil {
		return err
	}

	resp.Num, resp.CD = getInvestNum(role)
	return nil

}

func (h *PortServiceHandler) AddNum(ctx context.Context, _ *empty.Empty, resp *wrappers.Int32Value) error {

	// 101331 投资许可令
	if err := itemC.AsyncDelItem(h.GetRoleID(ctx), h.GetServerID(ctx), 101331, 1); err != nil {
		return err
	}

	var role = roleC.GetRole(h.GetRoleID(ctx))
	var n, _ = getInvestNum(role)

	if err := setInvestNum(role, n+30); err != nil {
		return err
	}

	resp.Value, _ = getInvestNum(role)
	return nil
}

func (h *PortServiceHandler) Refresh(ctx context.Context, req *empty.Empty, resp *pb.PortsResp) error {

	var role = roleC.GetRole(h.GetRoleID(ctx))
	var mess = getPorts(role)

	if mess.PortID != 0 {
		return errors.ParamError
	}

	// 101332 港口搜索券
	if err := itemC.AsyncDelItem(h.GetRoleID(ctx), h.GetServerID(ctx), 101332, 1); err != nil {
		return err
	}

	refresh(role.Level, mess)
	setPorts(role.RoleID, mess)

	return h.Ports(ctx, req, resp)
}

// Rand3个港口
func new3(role *pb.Role) []*pb.Port {

	var ports = make([]*pb.Port, 0, 3)
	for _, conf := range configuration.Rand3() {
		ports = append(ports, newPbPort(role.Level, conf.PortID))
	}

	return ports
}

// 刷新港口数据
func refresh(lv int32, data *pb.PortsResp) {

	var newPorts = make([]*pb.Port, 0, 3)

C:
	for _, conf := range configuration.Rand3() {

		for k, v := range data.Ports {
			if v != nil && conf.PortID == v.PortID {

				newPorts = append(newPorts, v)

				data.Ports[k] = nil
				continue C
			}
		}

		newPorts = append(newPorts, newPbPort(lv, conf.PortID))
	}

	for _, v := range data.Ports {
		if v != nil {
			newPorts = append(newPorts, v)
		}
	}

	data.Ports = newPorts
}

func getPorts(role *pb.Role) *pb.PortsResp {

	var res pb.PortsResp

	var b, err = redis.Get(fmt.Sprintf("3MyPorts%d", role.RoleID))
	if err != nil {
		panic(err)
	}

	if b == nil {

		res.Date = date.Date(time.Now())
		res.Ports = new3(role)
		setPorts(role.RoleID, &res)
	} else {

		_ = proto.Unmarshal(b, &res)
	}

	for _, v := range res.Ports {

		var costConf = configuration.GetByRoleLevel(role.Level)

		v.Coin = costConf.Coin
		v.Gold = costConf.Gold
	}

	return &res
}

func setPorts(roleID int32, mess *pb.PortsResp) {

	var b, _ = proto.Marshal(mess)
	_ = redis.Set(fmt.Sprintf("3MyPorts%d", roleID), b, 86400)
}

func maxNum(role *pb.Role) int32 {

	return configuration.GetByRoleLevel(role.Level).MaxInvestNum
}

const CD = time.Minute * 3

//// 投资次数
//type investNum struct {
//	Num       int32
//	Timestamp int32
//}

func getInvestNum(role *pb.Role) (num int32, sec int32) {

	var max = maxNum(role) // 最大次数

	var data, err = redis.Get(fmt.Sprintf("PortInvestNum%d", role.RoleID))
	if err != nil {
		panic(err)

	} else if data != nil {

		var t, _ = time.Parse(time.RFC3339Nano, string(data))
		return cd.NewCD(CD, max).SetTime(t).GetNum()
	}

	return max, 0
}

func setInvestNum(role *pb.Role, n int32) error {

	var max = maxNum(role)

	var data, err = redis.Get(fmt.Sprintf("PortInvestNum%d", role.RoleID))
	if err != nil {
		panic(err)

	} else if data != nil {

		var t, _ = time.Parse(time.RFC3339Nano, string(data))
		t = cd.NewCD(CD, max).SetTime(t).SetNum(n)

		data = []byte(t.Format(time.RFC3339Nano))

	} else {

		var t = cd.NewCD(CD, max).SetTime(time.Now()).SetNum(n)
		data = []byte(t.Format(time.RFC3339Nano))
	}

	return redis.Set(fmt.Sprintf("PortInvestNum%d", role.RoleID), data, int(time.Duration(max)*CD/time.Second))
}

func investNumSub1(role *pb.Role) error {

	var n, _ = getInvestNum(role)
	if n <= 0 {
		return fmt.Errorf("num is 0")
	}

	return setInvestNum(role, n-1)
}

func newPbPort(roleLevel, portID int32) *pb.Port {

	var conf = configuration.GetByPortID(portID)
	var cosConf = configuration.GetByRoleLevel(roleLevel)

	var itemID = conf.ItemID
	if itemID == 0 {
		itemID = consts.Gold
	}

	return &pb.Port{
		PortID: conf.PortID,
		Lv:     1,
		Gold:   cosConf.Gold,
		Coin:   cosConf.Coin,
		ItemID: itemID,
	}
}
